package vue.panneau2.modele.util;

import java.util.Iterator;
import java.util.ArrayList;

import vue.panneau2.modele.exception.FonctionObjectifDejaParametreeException;
import vue.panneau2.modele.exception.IndiceIncorrectException;
import vue.panneau2.modele.exception.MauvaiseDimensionException;

/**
 * Test de Matrice Generique (Dimension N)
 */
public class Simplexe {
	/**
	 * Dimension de la matrice : le nombre de variables de base qu'elle contiendra.
	 * Indique accessoirement la dimension des resultats que l'on obtiendra (2D, 3D, ...).
	 */
	private int dimension;
	/**
	 * Contient la solution de base realisable courante.
	 */
	private ArrayList<Float> solutionBase = new ArrayList<Float>();
	/**
	 * Contient l'historique des solutions de base realisable.
	 * Ces dernieres sont differentes a chaque iteration du simplexe.
	 */
	private ArrayList<ArrayList<Float>> historiqueSolutionBase = new ArrayList<ArrayList<Float>>();
	/**
	 * Indique la ligne du pivot courant.
	 */
	private int pivotLigne = -1 ;
	/**
	 * Indique la colonne du pivot courant.
	 */
	private int pivotColonne = -1 ;
	private ArrayList<Integer> historiquePivotLigne = new ArrayList<Integer>();
	private ArrayList<Integer> historiquePivotColonne = new ArrayList<Integer>();
	private ArrayList<Equation> matriceEquation = new ArrayList<Equation>();
	private ArrayList<ArrayList<Float>> matriceAugmentee = new ArrayList<ArrayList<Float>>();
	private ArrayList<ArrayList<ArrayList<Float>>> historiqueMatriceAugmentee = new ArrayList<ArrayList<ArrayList<Float>>>();
	/**
	 * Indique si la fonction objectif a été réglée dans l'instance courante
	 */
	private boolean fctObjReglee;
	/**
	 * Permet de nommer le problème de programmation linéaire courant.
	 */
	private String nomProb;
	/**
	 * Collection de noms indiquant les noms attribues aux variables.
	 */
	private ArrayList<String> nomVariables = new ArrayList<String>();
	/**
	 * Indique si des noms ont ete attribues aux variables de base.
	 */
	private boolean nomVariableRegle = false;
	/**
	 * Indique si le probleme de programmation lineaire a deja ete resolu.
	 * Est passe a faux lors de l'ajout ou de la suppression d'une equation,
	 * marquant ainsi le fait que la solution devra etre recalculee.
	 */
	private boolean resolu = false;
	
	/**
	 * Constructeur.
	 * @param dimension Dimension de la matrice : fixe le nombre de variables
	 * de base que la matrice pourra contenir.
	 */
	public Simplexe(int dimension) {
		this.dimension = dimension;
	}
	
	/**
	 * Constructeur.
	 * @param dimension Dimension de la matrice : fixe le nombre de variables
	 * de base que la matrice pourra contenir.
	 * @param nomVariables Une collection de noms indiquant les noms attribues
	 * aux variables de base.
	 */
	public Simplexe(int dimension, ArrayList<String> nomVariables) {
		this.dimension = dimension;
		this.nomVariables = nomVariables;
		nomVariableRegle = true;
	}

	/**
	 * Permet d'ajouter une equation a la matrice.
	 * Les equations de contraintes constituent toute la matrice a l'exception
	 * de la derniere ligne occupee par l'equation de la fonction objectif.
	 * Assure la conservation de la dimension choisie au depart.
	 * @throws FonctionObjectifDejaParametreeException 
	 * @throws MauvaiseDimensionException 
	 */
	public void ajouterEquation(Equation eq) throws FonctionObjectifDejaParametreeException, MauvaiseDimensionException {
		//Equation de la bonne dimension
		if (eq.getDimension() == dimension) {
			//Le problème de programmation linéaire n'est plus résolu
			resolu = false;
			//Equation non fonction objectif
			if (!eq.isFonctionObjectif()) {
				matriceEquation.add(eq);
			}
			else
			//Equation fonction objectif
			if (!fctObjReglee) {
				matriceEquation.add(eq);
				fctObjReglee = true;
			}
			else throw new FonctionObjectifDejaParametreeException();
		}
		else throw new MauvaiseDimensionException();
	}
	
	/**
	 * Permet de supprimer l'equation dont la reference est passee en parametre.
	 * @param eq Une reference sur l'equation a supprimer.
	 */
	public void supprimerEquation(Equation eq) {
		//Equation classique
		if (!eq.isFonctionObjectif()) {
			matriceEquation.remove(eq);
			resolu = false;
		}
		//Equation de la fonction objectif
		else {
			fctObjReglee = false;
			matriceEquation.remove(eq);
			resolu = false;
		}
	}
	
	/**
	 * Permet de supprimer l'equation a un indice index donne.
	 * @param index L'indice pour la suppression.
	 * @throws IndiceIncorrectException
	 */
	public void supprimerEquationAt(int index) throws IndiceIncorrectException {
		if (index >= 0 && index < getNbLignesMatriceAugmentee()) {
			matriceEquation.remove(index);
			resolu = false;
			//La fonction objectif est toujours la dernière de la matrice d'equation
			if (index == getNbLignesMatriceAugmentee() - 1) fctObjReglee = false;
		}
		else throw new IndiceIncorrectException("Tentative de suppression d'une equation en dehors des limites : "+index);
	}
	
	/**
	 * Permet de changer le nom de la variable de la colonne position.
	 * @param position La position de la modification.
	 * @param nom Le nouveau nom de la variable.
	 * @throws IndiceIncorrectException
	 */
	public void setNomVariable(int position, String nom) throws IndiceIncorrectException {
		if (position >= 0 && position < dimension) {
			try {
				nomVariables.set(position, nom);
			} catch (IndexOutOfBoundsException e) {
				nomVariables.add(position, nom);
			}
		}
		else throw new IndiceIncorrectException("Tentative de modification du nom d'une variable en dehors des limites : "+position);
	}
	
	/**
	 * Permet d'acceder au nom de la variable de la colonne position.
	 * @param position L'indice de la colonne dont on desire le nom.
	 * @return Le nom de la variable sous forme de chaine de caracteres.
	 * @throws IndiceIncorrectException
	 */
	public String getNomVariable(int position) throws IndiceIncorrectException {
		if (position >= 0 && position < getNbColonnesMatriceAugmentee() - 1) {
			return nomVariables.get(position);
		}
		else throw new IndiceIncorrectException("Tentative d'acces au nom d'une variable en dehors des limites : "+position);
	}
	
	/**
	 * Convertit l'equation de la fonction objectif ainsi que les equations
	 * de contraintes vers un systeme d'equation et regle la matrice de base.
	 * Etape 1 et 2 de la methode du simplexe.
	 */
	private void convertirVersSystemeEquation() {
		Iterator<Equation> iterEq = matriceEquation.iterator();
		//Nombre de variable supplementaires = nombre de lignes de la matrice = nombre d'equation
		int nbVarSup = matriceEquation.size();
		int decalage = 0;

		while (iterEq.hasNext()) {
			Equation eq = iterEq.next();
			//Represente une ligne de la matrice systeme d'equation
			ArrayList<Float> vectLigneMatrice = new ArrayList<Float>();
			
			System.out.println("Coefficients "+eq.getCoefficients());
			
			//Pour toutes les lignes sauf la derniere (celle de la fonction objectif)
			if (decalage < nbVarSup - 1) vectLigneMatrice.addAll(eq.getCoefficients());
			else {
				//On inverse le signe des coefficients de la fonction objectif
				Iterator<Float> iterFloat = eq.getCoefficients().iterator();
				while (iterFloat.hasNext()) {
					vectLigneMatrice.add(-iterFloat.next());
				}
			}
			
			System.out.println("Premiere partie nouvelle ligne"+vectLigneMatrice.toString());
			
			//On rajoute les variables additionnelles constituant le centre de la matrice
			for (int i = 0; i < nbVarSup; i++) {
				//Variable additionnelle classique
				if (i == decalage) {
					if (eq.getSigne() == Equation.INF_OU_EGAL) vectLigneMatrice.add(1.0f);
					else vectLigneMatrice.add(-1.0f);
				}
				//Variable additionnelle fonction objectif
				else if ((decalage == nbVarSup)) {
					if (eq.isMaximiser()) vectLigneMatrice.add(1.0f);
					else vectLigneMatrice.add(-1.0f);
				}
				//Pour remplir les vides
				else vectLigneMatrice.add(0.0f);
			}
			
			System.out.println("Ajout Partie centrale nouvelle ligne"+vectLigneMatrice.toString());
			
			//On rajoute la partie droite des equations et on met 0.0 pour la fonction objectif
			if (decalage < nbVarSup - 1) vectLigneMatrice.add(eq.getPartieDroite());
			else vectLigneMatrice.add(0.0f);
			matriceAugmentee.add(vectLigneMatrice);
			System.out.println("Ajout ligne au systeme d'equation : "+vectLigneMatrice.toString());

			decalage++;
		}
		
		//Sauvegarde initiale
		lancerSauvegardeMatrice();
		
		/*
		 * Si celui-ci n'est pas deja rempli, initialise a "X+i" le tableau du nom des variables.
		 * Permet d'eviter une IndexOutOfBoundsException lors d'une tentative de modification du nom d'une variable.
		 */
		if (!nomVariableRegle) {
			for (int i = 0; i < getNbColonnesMatriceAugmentee() - 1; i++) {
				nomVariables.add("X"+i);
			}
			nomVariableRegle = true;
		}
	}

	/**
	 * Sauvegarde l'ancienne matrice (matrice intermediaire) dans le vecteur historique avant le recalcul
	 * de cette derniere (iteration suivante de l'algorithme du simplexe)
	 */
	private void lancerSauvegardeMatrice() {
		ArrayList<ArrayList<Float>> matriceAugmenteeCopy = new ArrayList<ArrayList<Float>>();
		ArrayList<Float> ligneMatriceAugmenteeCopy = new ArrayList<Float>();
		
		for (ArrayList<Float> ligne : matriceAugmentee) {
			for (Float coef : ligne) {
				ligneMatriceAugmenteeCopy.add(new Float(coef.floatValue()));
			}
			matriceAugmenteeCopy.add(ligneMatriceAugmenteeCopy);
			ligneMatriceAugmenteeCopy = new ArrayList<Float>();
		}
		
		historiqueMatriceAugmentee.add(matriceAugmenteeCopy);
	}
	
	/**
	 * Permet d'obtenir une representation du systeme d'equation de base sous forme
	 * de chaine de caractere.
	 * @return Une representation du systeme d'equation de base sous forme
	 * de chaine de caractere.
	 */
	public ArrayList<String> getSystemeEquation() {
		ArrayList<String> res = new ArrayList<String>();
		
		if (!nomVariableRegle) {
			for (Equation eq : matriceEquation) {
				if (!eq.isFonctionObjectif()) res.add(eq.afficheEquation());
				else res.add(0, eq.afficheEquation());
			}
		}
		else {
			for (Equation eq : matriceEquation) {
				if (!eq.isFonctionObjectif()) res.add(eq.afficheEquation(nomVariables));
				else res.add(0, eq.afficheEquation(nomVariables));
			}
		}
		return res;
	}
	
	/**
	 * Retourne une representation de la matrice actuelle sous forme de chaine de caracteres
	 * @return Une chaine de caractere representant les lignes et les colonnes de la matrice
	 */
	public ArrayList<String> afficheMatriceAugmentee() {
		ArrayList<String> res = new ArrayList<String>();
		
		String resInter = "";
		for (ArrayList<Float> ligne : matriceAugmentee) {
			for (Float coef : ligne) {
				resInter += "["+coef.toString()+"]   ";
			}
			res.add(resInter);
			resInter = "";
		}
		return res;
	}
	
	/**
	 * Permet de recuperer la collection contenant les valeurs de la matrice augmentee
	 * @return Une reference vers la collection contenant les valeurs de la matrice augmentee
	 */
	public ArrayList<ArrayList<Float>> getMatriceAugmentee() {
		return matriceAugmentee;
	}
	
	/**
	 * Retourne la solution de base realisable courante sous forme de chaine de caractere,
	 * cette derniere dependant des valeurs presentes dans la matrice.
	 * Necessite d'avoir deja calcule la solution de base realisable a l'aide
	 * la methode trouverLaSolutionBasique().
	 * @return La solution initiale sous forme de chaine de caractere
	 */
	public String afficheSolutionBaseRealisableCourante() {
		String res = "" ;
		for (Float val : solutionBase) {
			res += val + "  ";
		}
		return res ;
	}

	/**
	 * Calcule la solution de base realisable sur la matrice courante.
	 * La methode afficheSolutionInitiale() permet d'afficher cette meme solution.
	 * Si la collection contient deja une solution de base realisable,
	 * stocke cette derniere dans une autre collection afin de constituer
	 * un historique des solutions de bases realisables.
	 * @see afficheSolutionInitiale()
	 */
	private void trouverLaSolutionBaseRealisable() {

		
		
		
		//Remise a zero de la collection pour l'iteration suivante du simplexe
		solutionBase = new ArrayList<Float> ();
		
		boolean[] lignesEpuisees;
		lignesEpuisees = new boolean[getNbLignesMatriceAugmentee()];
		for (int i = 0; i < getNbLignesMatriceAugmentee(); i++) {
			// aucune ligne epuisee
			lignesEpuisees[i] = false ;
		}

		for (int colonne = 0; colonne < getNbColonnesMatriceAugmentee()-1; colonne++) {
			int nombreDEntreeDifferenteDeZero = 0 ;
			int ligneDeLEntreeDifferenteDeZero = 0 ;
			for (int ligne = 0; ligne < getNbLignesMatriceAugmentee(); ligne++) {
				
				// si la colonne est differente de 0 !
				if (matriceAugmentee.get(ligne).get(colonne) != 0.0f) {
					nombreDEntreeDifferenteDeZero++;
					ligneDeLEntreeDifferenteDeZero = ligne;
				}
			}
			if (nombreDEntreeDifferenteDeZero == 1 && !lignesEpuisees[ligneDeLEntreeDifferenteDeZero])
			{
				System.out.println("colonne "+colonne+": on divise "+matriceAugmentee.get(ligneDeLEntreeDifferenteDeZero).get(getNbColonnesMatriceAugmentee() - 1)+" par "+matriceAugmentee.get(ligneDeLEntreeDifferenteDeZero).get(colonne));
				solutionBase.add(colonne, matriceAugmentee.get(ligneDeLEntreeDifferenteDeZero).get(getNbColonnesMatriceAugmentee() - 1)/matriceAugmentee.get(ligneDeLEntreeDifferenteDeZero).get(colonne));
				lignesEpuisees[ligneDeLEntreeDifferenteDeZero] = true ;
			}else {
				System.out.println("colonne "+colonne+": vaut 0 " ) ;
				solutionBase.add(colonne, 0.0f);
			}
		}
		
		System.out.println("Ajout solution base");
		//Sauvegarde de la solution de base realisable courante
		historiqueSolutionBase.add(solutionBase);
		
		System.out.println("Solution base\n"+solutionBase);
		
	}

	/**
	 * Selectionne le pivot, lorsque c'est possible.
	 * @return Vrai si l'on peut trouver un pivot.
	 */
	private boolean choisirPivot() {
		// d'abord le choix de la colonne:
		// il faut exclure Ans,
		// prendre le nombre negatif le plus petit,
		// si on n a que des positifs ou nuls,
		// la solution est deja optimale...

		int colonneLaPlusNegative = -1 ;
		for (int colonne = 0; colonne < getNbColonnesMatriceAugmentee() - 1; colonne++) {
			System.out.println("examen de la colonne "+colonne);
			if (matriceAugmentee.get(getNbLignesMatriceAugmentee() - 1).get(colonne) < 0.0f) {
				System.out.println("la colonne "+colonne+" est negative");
				if (colonneLaPlusNegative == -1) {
					colonneLaPlusNegative = colonne ;
				}
				else {
					if (matriceAugmentee.get(getNbLignesMatriceAugmentee() - 1).get(colonneLaPlusNegative) >= matriceAugmentee.get(getNbLignesMatriceAugmentee() - 1).get(colonne))
						colonneLaPlusNegative = colonne ;
				}
			}
		}
		
		if (colonneLaPlusNegative == -1) {
			//on a deja la solution la plus optimale
			System.err.println("solution optimale trouvee");
			return false ;
		}

		//sinon on a la colonne du pivot
		//reste la ligne
		
		//la valeur de la ligne doit etre positive:
		//on prend celle ou le ratio (Ans/pivot est la plus petite)
		
		// pour chaque ligne de la colonne du pivot
		int ligneOptimale = -1 ;
		for (int ligne = 0; ligne < getNbLignesMatriceAugmentee(); ligne++) {
			// si le nombre est positif
			// si il est negatif ou nul on ne peut pas le prendre!
			if (matriceAugmentee.get(ligne).get(colonneLaPlusNegative) > 0.0f) {
				if (ligneOptimale == -1) {
					ligneOptimale = ligne ;
				}
				else {
					// on fait le ratio,
					// si le resultat est plus petit,
					// on change l optimal
					if (
(matriceAugmentee.get(ligne).get(getNbColonnesMatriceAugmentee() - 1)/matriceAugmentee.get(ligne).get(colonneLaPlusNegative)) < (matriceAugmentee.get(ligneOptimale).get(getNbColonnesMatriceAugmentee() - 1)/matriceAugmentee.get(ligneOptimale).get(colonneLaPlusNegative)))
					{
						ligneOptimale = ligne ;
					}
				}
			}
		}

		// si on a rien trouve :
		if (ligneOptimale == -1) {
			System.err.println("on a pas pu trouver de pivot");
			return false ;
		}

		// sinon le pivot est 
		//(ligneOptimale,colonneLaPlusNegative)
		pivotLigne = ligneOptimale ;
		pivotColonne = colonneLaPlusNegative ;
		
		historiquePivotLigne.add(pivotLigne);
		historiquePivotColonne.add(pivotColonne);
		
		System.out.println("matrice augmentee");
		for (String s : afficheMatriceAugmentee()) {
			System.out.println("["+s+"]");
		}
		System.out.println("fin matrice augmentee pivot == "+pivotLigne+","+pivotColonne);
		
		
		
		System.err.println("pivot trouve");
		return true ;
	}

	/**
	 * Effectue le pivot
	 */
	private void effectuerLePivot() {
		if (pivotLigne == -1 || pivotColonne == -1)
		{
			System.err.println("pas de pivot specifie");
			return ;
		}

		System.out.println("matrice precedante : (l="+pivotLigne+",c="+pivotColonne+")");
		this.afficheMatriceAugmentee2();
		
		
		float pivot = matriceAugmentee.get(pivotLigne).get(pivotColonne);
		for (int ligneChange = 0; ligneChange < getNbLignesMatriceAugmentee(); ligneChange++) {
			//Pour toutes les lignes differentes de pivotLigne
			if (ligneChange != pivotLigne) {

				
				float ligneChangeColonnePivot  = matriceAugmentee.get(ligneChange).get(pivotColonne) ;
				
				System.out.println("pivot="+pivot+" autre-coeff="+ligneChangeColonnePivot);
				
				
				//Pour toutes les colonnes
				for (int colonneChange = 0; colonneChange < getNbColonnesMatriceAugmentee(); colonneChange++) {
					//ordre : 1-ligne à changer 2-ligne pivot

					float ligneChangeColonneChange = matriceAugmentee.get(ligneChange).get(colonneChange) ;
					float lignePivotColonneChange  = matriceAugmentee.get(pivotLigne).get(colonneChange) ;

					
					
						matriceAugmentee.get(ligneChange).set(colonneChange,
								(pivot 
										* ligneChangeColonneChange)
								- 
								(ligneChangeColonnePivot
										* lignePivotColonneChange) );

				}
				System.out.println("ligne changee ["+ligneChange+"] :");
				for (Float fl : matriceAugmentee.get(ligneChange)) {
					System.out.print(fl+"::");
				}
				System.out.println();
				
			}

			
		}

		
		System.out.println("matrice suivante :");
		this.afficheMatriceAugmentee2();
//		try {
//			Thread.sleep(99999999) ;
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		

		//On sauvegarde la nouvelle matrice
		lancerSauvegardeMatrice() ;
		
		pivotColonne = -1 ;
		pivotLigne = -1 ;
	}

	private void afficheMatriceAugmentee2() {
		for (String s : this.afficheMatriceAugmentee()) {
			System.out.println(s);
		}
		return ;
	}

	private boolean memeSigne(float nb1, Float nb2) {
		return (nb1*nb2>0);
	}

	/**
	 * Repete les etapes choix du pivot et de realisation du pivot jusqu'a l'obtention de la matrice finale.
	 * L'operation est terminee des qu'il n'y plus de nombre negatif dans la derniere ligne du bas
	 * (à l'exception de la derniere colonne, la colonne reponse).
	 */
	private void trouverTableauFinal() {
		int ind = 1 ;
		trouverLaSolutionBaseRealisable();
		while (choisirPivot()) {
			System.out.println("tour numero "+ind);
			effectuerLePivot() ;
			//Trouve la solution de base realisable associee a la matrice courante.
			trouverLaSolutionBaseRealisable();
		}
		System.out.println("fin ...");
	}
		
	/**
	 * Permet d'obtenir l'historique des versions successives de la matrice au cours des differentes iterations
	 * de la methode du simplexe.
	 * @return Un historique du contenu de la matrice sous forme de chaine de caractere.
	 */
	public ArrayList<String> imprimeHistorique() {
		ArrayList<String> res = new ArrayList<String>();
		
		String resInter = "";
		for (ArrayList<ArrayList<Float>> mat : historiqueMatriceAugmentee) {
			for (ArrayList<Float> ligne : mat) {
				for (Float coef : ligne) {
					resInter += "["+coef+"]";
				}
			}
			res.add(resInter);
			resInter = "";
		}
		
		return res ;
	}
	
	/**
	 * Resoud le probleme courant de programmation lineaire en utilisant la methode du simplexe.
	 * Prend en compte le phenomene de resolutions successives en reactualisant les structures de
	 * donnees intermediaires.
	 */
	public void resoudre() {
		//Nettoyage historiques, etc pour reactualiser la resolution en cas de changement des equations
		historiqueMatriceAugmentee = new ArrayList<ArrayList<ArrayList<Float>>>();
		historiquePivotColonne = new ArrayList<Integer>();
		historiquePivotLigne = new ArrayList<Integer>();
		historiqueSolutionBase = new ArrayList<ArrayList<Float>>();
		matriceAugmentee = new ArrayList<ArrayList<Float>>();
		
		/*
		 * Conversion du systeme d'equation courant vers le systeme d'equation augmente
		 * et resolution a proprement parler
		 */ 
		convertirVersSystemeEquation();
		trouverTableauFinal();
		resolu = true;
	}
	
	/**
	 * Permet de connaitre le nombre de matrice intermediaires obtenues pendant
	 * l'application de la methode du simplexe.
	 * Necessite d'avoir prealablement resolu le probleme par l'appel de la methode
	 * resoudre().
	 * @return Le nombre de matrice intermediaires.
	 */
	public int getnombreDeMatrices() {
		return historiqueMatriceAugmentee.size();
	}
	
	/**
	 * Permet de recuperer une matrice intermediaire pour une etape numeroEtape de l'algorithme donnee.
	 * @param numeroEtape Le numero de l'etape de l'algorithme correspondant aussi a l'indice d'une matrice de l'historique.
	 * @return Une matrice contenant les valeurs du systeme d'equation a une etape numeroEtape donnee.
	 * @throws IndiceIncorrectException 
	 */
	public ArrayList<String> afficheMatriceAugmenteeEtape(int numeroEtape) throws IndiceIncorrectException {
		ArrayList<ArrayList<Float>> matrice = null;

		try {
			matrice = historiqueMatriceAugmentee.get(numeroEtape);
		} catch (ArrayIndexOutOfBoundsException e) {
			throw new IndiceIncorrectException("Indice de matrice demande incorrect : l'etape "+numeroEtape+" n'existe pas");
		}

		ArrayList<String> res = new ArrayList<String>();
		for (int i = 0; i < getNbLignesMatriceAugmentee(); i++) {
			for (int j = 0; j < getNbColonnesMatriceAugmentee(); j++) {
				res.add(matrice.get(i).get(j).toString());
			}
		}

		return res;
	}

	/**
	 * Permet de récuperer la matrice augmentée pour l'étape numeroEtape
	 * @param numeroEtape Le numéro de l'étape pour lequel on veut recupérer la
	 * matrice augmentée.
	 * @return La matrice augmentée sous forme de ArrayList<ArrayList<Float>>.
	 * @throws IndiceIncorrectException 
	 */
	public ArrayList<ArrayList<Float>> getMatriceAugmenteeEtape(int numeroEtape) throws IndiceIncorrectException {
		if (numeroEtape >= 0 && numeroEtape < historiqueMatriceAugmentee.size()) return historiqueMatriceAugmentee.get(numeroEtape);
		else throw new IndiceIncorrectException("Tentative d'acces a une matrice en dehors des limites de historique : "+numeroEtape);
	}
	
	/**
	 * Permet de recuperer le numero de ligne du pivot pendant l'etape numeroEtape.
	 * @param numeroEtape Le numero de l'etape de l'algorithme correspondant aussi a l'indice d'une matrice de l'historique.
	 * @return Un entier indiquant le numero de ligne du pivot pendant l'etape numeroEtape.
	 * @throws IndiceIncorrectException
	 */
	public int getPivotLigneEtape(int numeroEtape) throws IndiceIncorrectException {
		int ligne = 0;
		
		try {
			ligne = historiquePivotLigne.get(numeroEtape);
		} catch (IndexOutOfBoundsException e) {
			throw new IndiceIncorrectException("Indice de matrice demande incorrect : l'etape "+numeroEtape+" n'existe pas");
		}
		
		return ligne;
	}
	
	/**
	 * Permet de recuperer le numero de colonne du pivot pendant l'etape numeroEtape.
	 * @param numeroEtape Le numero de l'etape de l'algorithme correspondant aussi a l'indice d'une matrice de l'historique.
	 * @return Un entier indiquant le numero de colonne du pivot pendant l'etape numeroEtape.
	 * @throws IndiceIncorrectException 
	 */
	public int getPivotColonneEtape(int numeroEtape) throws IndiceIncorrectException {
		int colonne = 0;
		
		try {
			colonne = historiquePivotLigne.get(numeroEtape);
		} catch (IndexOutOfBoundsException e) {
			throw new IndiceIncorrectException("Indice de matrice demande incorrect : l'etape "+numeroEtape+" n'existe pas");
		}
		
		return colonne;
	}

	/**
	 * Accesseur en lecture du nom du probleme courant.
	 * @return Le nom du probleme courant.
	 */
	public String getNomProb() {
		return nomProb;
	}

	/**
	 * Accesseur en ecriture sur le nom du probleme courant.
	 * @param nomProb Le nouveau nom du probleme courant.
	 */
	public void setNomProb(String nomProb) {
		this.nomProb = nomProb;
	}

	/**
	 * Permet de recuperer la dimension de la matrice (2D, 3D, ...),
	 * c'est a dire le nombre de variable de base dans chaque equation
	 * qu'elle pourra contenir.
	 * @return Un entier indiquant la dimension de la matrice (et donc des
	 * equations qu'elle contient).
	 */
	public int getDimension() {
		return dimension;
	}
	
	/**
	 * Permet d'obtenir des informations a propos du calcul effectue a partir du pivot
	 * lors de l'etape numeroEtape.
	 * Exemple : L3 <- 3/2 * L3 + 1/3 * L2.
	 * @param numeroEtape Entier indiquant l'etape (en nombre d'iteration de l'algorithme du simplexe)
	 * pour laquelle on veut obtenir des informations.
	 * @return Les informations sous forme de chaine de caractere.
	 * @throws IndiceIncorrectException 
	 */
	public String affichePivotageEtape(int numeroEtape) throws IndiceIncorrectException {
		if (numeroEtape >= 0 && numeroEtape < historiqueMatriceAugmentee.size()) {
			ArrayList<ArrayList<Float>> matriceEtape = historiqueMatriceAugmentee.get(numeroEtape);
			int pivotLigneEtape = getPivotLigneEtape(numeroEtape);
			int pivotColonneEtape = getPivotColonneEtape(numeroEtape);
			float valeurPivot = matriceEtape.get(pivotLigneEtape).get(pivotColonneEtape);
			
			String res = "";
			for (int i = 0; i < getNbLignesMatriceAugmentee(); i++) {
				if (i != pivotLigneEtape) {
					res += valeurPivot+"L"+i+" + "+-matriceEtape.get(i).get(pivotColonneEtape)+"L"+pivotLigneEtape;
				}
				res += "\n";
			}
			return res;
		}
		else throw new IndiceIncorrectException("Tentative d'acces a une matrice en dehors des limites de historique : "+numeroEtape);
	}
	
	/**
	 * Permet d'obtenir des informations a propos du calcul effectue a partir du pivot
	 * lors de l'etape numeroEtape.
	 * Exemple : L3 <- 3/2 * L3 + 1/3 * L2.
	 * @param numeroEtape Entier indiquant l'etape (en nombre d'iteration de l'algorithme du simplexe)
	 * pour laquelle on veut obtenir des informations.
	 * @return Les informations sous forme d'un ArrayList de String.
	 * @throws IndiceIncorrectException 
	 */
	public ArrayList<String> getPivotageEtape(int numeroEtape) throws IndiceIncorrectException {
		if (numeroEtape >= 0 && numeroEtape < historiqueMatriceAugmentee.size()) {
			ArrayList<ArrayList<Float>> matriceEtape = historiqueMatriceAugmentee.get(numeroEtape);
			ArrayList<String> pivotEtape = new ArrayList<String>();
			int pivotLigneEtape = getPivotLigneEtape(numeroEtape);
			int pivotColonneEtape = getPivotColonneEtape(numeroEtape);
			float valeurPivot = matriceEtape.get(pivotLigneEtape).get(pivotColonneEtape);
			
			for (int i = 0; i < getNbLignesMatriceAugmentee(); i++) {
				if (i != pivotLigneEtape) {
					pivotEtape.add("L"+i+" <- "+valeurPivot+"L"+i+" + "+-matriceEtape.get(i).get(pivotColonneEtape)+"L"+pivotLigneEtape);
				}
				else pivotEtape.add("L"+i+" <- L"+i);
			}
			return pivotEtape;
		}
		else throw new IndiceIncorrectException("Tentative d'acces a une matrice en dehors des limites de historique : "+numeroEtape);
	}
	
	/**
	 * Permet d'obtenir le calcul des ratios qui ont permis le choix du pivot a l'iteration
	 * numeroEtape de l'algorithme du simplexe.
	 * @param numeroEtape Le numero de l'iteration dont on desire recuperer les informations
	 * sur le choix du pivot.
	 * @return Les informations sous forme de chaine de caractere.
	 * @throws IndiceIncorrectException 
	 */
	public String afficheRatioMatriceEtape(int numeroEtape) throws IndiceIncorrectException {
		if (numeroEtape >= 0 && numeroEtape < historiqueMatriceAugmentee.size()) {
			ArrayList<ArrayList<Float>> matriceEtape = historiqueMatriceAugmentee.get(numeroEtape);
			int pivotLigneEtape = getPivotLigneEtape(numeroEtape);
			int pivotColonneEtape = getPivotColonneEtape(numeroEtape);
			
			String res = "";
			for (int i = 0; i < getNbLignesMatriceAugmentee(); i++) {
				//On ne s'interesse qu'aux lignes dont la valeur dans la colonne pivotColonne est strictement positif.
				if (matriceEtape.get(i).get(pivotColonneEtape) > 0.0f) {
					//Calcul du ratio : colonne "answer"/colonne pivot = ratio
					res += "Test Ratio = "+matriceEtape.get(i).get(getNbColonnesMatriceAugmentee() - 1)
					+"/"+matriceEtape.get(i).get(pivotColonneEtape)
					+" = "+(matriceEtape.get(i).get(getNbColonnesMatriceAugmentee() - 1)/matriceEtape.get(i).get(pivotColonneEtape));
					if (i == pivotLigneEtape) res += " (Meilleur ratio)";
				}
				res += "\n";
			}
			return res;
		}
		else throw new IndiceIncorrectException("Tentative d'acces a une matrice en dehors des limites de historique : "+numeroEtape);
	}
	
	/**
	 * Permet d'obtenir le calcul des ratios qui ont permis le choix du pivot a l'iteration
	 * numeroEtape de l'algorithme du simplexe.
	 * @param numeroEtape Le numero de l'iteration dont on desire recuperer les informations
	 * sur le choix du pivot.
	 * @return Les informations sous forme d'ArrayList de String.
	 * @throws IndiceIncorrectException
	 */
	public ArrayList<String> getRatioMatriceEtape(int numeroEtape) throws IndiceIncorrectException {
		if (numeroEtape >= 0 && numeroEtape < historiqueMatriceAugmentee.size()) {
			ArrayList<ArrayList<Float>> matriceEtape = historiqueMatriceAugmentee.get(numeroEtape);
			ArrayList<String> ratiosEtape = new ArrayList<String>();
			int pivotLigneEtape = getPivotLigneEtape(numeroEtape);
			int pivotColonneEtape = getPivotColonneEtape(numeroEtape);
			
			for (int i = 0; i < getNbLignesMatriceAugmentee(); i++) {
				//On ne s'interesse qu'aux lignes dont la valeur dans la colonne pivotColonne est strictement positif.
				if (matriceEtape.get(i).get(pivotColonneEtape) > 0.0f) {
					//Calcul du ratio : colonne "answer"/colonne pivot = ratio
					String ratio = matriceEtape.get(i).get(getNbColonnesMatriceAugmentee() - 1)+"/"+matriceEtape.get(i).get(pivotColonneEtape)
					+" = "+(matriceEtape.get(i).get(getNbColonnesMatriceAugmentee() - 1)/matriceEtape.get(i).get(pivotColonneEtape));
					if (i == pivotLigneEtape) ratio += " (Meilleur ratio)";
					ratiosEtape.add(ratio);
				}
			}
			return ratiosEtape;
		}
		else throw new IndiceIncorrectException("Tentative d'acces a une matrice en dehors des limites de historique : "+numeroEtape);
	}

	/**
	 * Permet d'obtenir un message d'explication a chaque etape cle de la methode
	 * du simplexe.
	 * Etapes cles :
	 * -> Groupe 1 :
	 * 		1-2) Saisie du problème de programmation linéaire sous forme d'équations
	 * 		et conversion de l'ensemble des équations de départ pour obtenir la matrice
	 * 		augmentée (Variables supplémentaires, etc ...).
	 * -> Groupe 2 : 
	 * 		3) Choix de la colonne du pivot.
	 * 		4) Choix du pivot dans la colonne du pivot.
	 * 		5) Nettoyage de la colonne du pivot (a la fin de cette etape, il reste le
	 * 		pivot a sa valeur initiale, toutes les autres valeurs de la colonne sont a 0).
	 * -> Groupe 3 :
	 * 		6) Répéter 3-5 jusqu'à ce qu'il n'y ait plus de nombres négatifs dans la
	 * 		dernière ligne (Avec l'exception de la colonne réponse).
	 * @param numeroEtape Un entier indiquant l'etape pour laquelle on desire obtenir une phrase d'explication.
	 * Par convention, la phase de mise sous forme de matrice augmentee sera notee -1, numEtape
	 * indiquant alors le numero de la matrice augmentee intermediaire depuis la premiere notee 0.
	 * @return Une phrase explicative sous forme de chaine de caracteres.
	 * @throws IndiceIncorrectException 
	 */
	public ArrayList<String> getPhraseExplicativeEtape(int numeroEtape) throws IndiceIncorrectException {
		ArrayList<String> res = new ArrayList<String>();
		//Debut de la methode du simplexe : pas de matrice augmentee, on presente le systeme d'equation, etc ...
		//Etapes 1-2 methode du simplexe
		if (numeroEtape == -1) {
			res.add("On doit convertir l'ensemble des equations de depart");
			res.add("de maniere a obtenir la matrice augmentee.");
			res.add("La matrice augmentee comporte toute les variables de base,");
			res.add("c'est a dire toutes les variables presente dans les equations et y adjoint");
			res.add("des variables additionnelles a raison de une par equation");
			res.add("");
			res.add("Systeme d'equation initial : ");
			res.addAll(getSystemeEquation());
			res.add("");
			res.add("Apres conversion vers une matrice augmentee, cela donne : ");
			res.addAll(afficheMatriceAugmenteeEtape(0));
			res.add("On obtient a la fin de cette operation la solution de base realisable suivante : ");
			res.add(afficheSolutionBaseRealisableEtape(0));
		}
		else
		//Une des matrices augmentees intermediaires
		//Etapes 3-5 methode du simplexe
		if (numeroEtape >= 0 && numeroEtape < getnombreDeMatrices()-1) {
			res.add("Choix de la colonne du pivot : ");
			res.add("Regle : Parmi tout les coefficient de la ligne du bas, sauf celui de la colonne \"reponse\", la derniere, choisir celui qui est le plus negatif");
			res.add("Cette Colonne est ici : "+getPivotColonneEtape(numeroEtape));
			res.add("Choix du pivot : ");
			res.add("Regle 1 : Le pivot est un nombre strictement positif");
			res.add("Regle 2 : Le choix du pivot s'effectue ensuite en selectionnant la ligne qui obtient le plus petit ratio");
			res.add("Les ratios sont : ");
			res.add(afficheRatioMatriceEtape(numeroEtape));
			res.add("D'ou le choix de la ligne pour le pivot " + getPivotLigneEtape(numeroEtape));
			res.add("Et le pivot associe "+ matriceAugmentee.get(getPivotLigneEtape(numeroEtape)).get(getPivotColonneEtape(numeroEtape)));
			res.add("Nettoyage de la colonne du pivot, a la fin toutes les valeurs autres que celle du pivot sont a 0 : ");
			res.add(affichePivotageEtape(numeroEtape));
			res.add("On obtient a la fin de ces operation la solution de base realisable suivante : ");
			res.add(afficheSolutionBaseRealisableEtape(numeroEtape));
		}
		else
		//La matrice augmentee finale + conclusion
		//Etape 6 methode du simplexe
		if (numeroEtape == getnombreDeMatrices() - 1) {
			res.add("Matrice finale");
			res.addAll(afficheMatriceAugmentee());
			res.add("Obtention de la solution finale au probleme : ");
			res.add(afficheSolutionBaseRealisableCourante());
			res.add("La methode du simplexe est terminee car il n'y plus de nombre negatif dans la derniere ligne de la matrice (avec la possible exception de la colonne reponse, la derniere).");
		}
		//Depassement des limites
		else throw new IndiceIncorrectException("Tentative d'acces a une matrice en dehors des limites de historique : "+numeroEtape);
		
		return res;
	}
	
	/**
	 * Permet de recuperer les coordonnees du nouveau point solution obtenu lors de l'etape numeroEtape.
	 * @param numeroEtape Un entier indiquant l'etape pour laquelle on desire obtenir les coordonnees d'un nouveau point realisable.
	 * @return La liste des coordonnees du nouveau point solution obtenu lors de l'etape numeroEtape sous forme de
	 * collection.
	 * @throws IndiceIncorrectException 
	 */
	public ArrayList<Float> getSolutionBaseRealisableEtape(int numeroEtape) throws IndiceIncorrectException {
		if (numeroEtape >= 0 && numeroEtape < historiqueSolutionBase.size()) {
			return historiqueSolutionBase.get(numeroEtape);
		}
		else throw new IndiceIncorrectException("Tentative d'acces a une matrice en dehors des limites de historique : "+numeroEtape);
	}
	
	/**
	 * Affiche les coordonnees du nouveau point solution obtenu lors de l'etape numeroEtape.
	 * @param numeroEtape Un entier indiquant l'etape pour laquelle on desire obtenir les coordonnees d'un nouveau point realisable.
	 * @return La liste des coordonnees du nouveau point solution obtenu lors de l'etape numeroEtape sous forme de
	 * chaine de caracteres.
	 * @throws IndiceIncorrectException 
	 */
	public String afficheSolutionBaseRealisableEtape(int numeroEtape) throws IndiceIncorrectException {
		if (numeroEtape >= 0 && numeroEtape < historiqueSolutionBase.size()) {
			ArrayList<Float> solBase = getSolutionBaseRealisableEtape(numeroEtape);
			String res = "";
			
			for (Float val : solBase) {
				res += val + "   ";
			}
			
			return res;
		}
		else throw new IndiceIncorrectException("Tentative d'acces a une matrice en dehors des limites de historique : "+numeroEtape);
	}
	
	/**
	 * Permet d'acceder au nombre de solution de base realisables stockees dans l'historique.
	 * @return Un entier indiquant le nombre de solution de base realisables calculees et stockees
	 * dans l'historique.
	 */
	public int getNbSolutionBaseRealisable() {
		return historiqueSolutionBase.size();
	}

	/**
	 * Accesseur en lecture sur l'attribut résolu
	 * @return Vrai ssi le probleme de programmation linéaire a déjà été résolu
	 */
	public boolean isResolu() {
		return resolu;
	}

	/**
	 * Permet d'obtenir le nombre de lignes de la matrice augmentée
	 * @return Un entier indiquant le nombre de lignes de la matrice augmentée
	 */
	public int getNbLignesMatriceAugmentee() {
		return matriceAugmentee.size();
	}

	/**
	 * Permet d'obtenir le nombre de colonnes de la matrice augmentée
	 * @return Un entier indiquant le nombre de colonnes de la matrice augmentée
	 */
	public int getNbColonnesMatriceAugmentee() {
		return matriceAugmentee.get(0).size();
	}
	
	/**
	 * Permet de récupérer l'équation fonction objectif
	 * @return L'équation de la fonction objectif
	 */
	public Equation getFonctionObjectif() {
		//La fonction objectif a été réglée
		if (fctObjReglee) return matriceEquation.get(matriceEquation.size() - 1);
		else return null;
	}
	
	/**
	 * Permet d'obtenir la valeur de la fonction objectif à l'étape numeroEtape.
	 * @param numeroEtape L'étape pour laquelle on désire connaître la valeur
	 * de la fonction objectif.
	 * @return La valeur de la fonction objectif à l'étape numeroEtape.
	 * @throws IndiceIncorrectException
	 */
	public Float getValeurFonctionObjectifEtape(int numeroEtape) throws IndiceIncorrectException {
		Equation eqFctObj = getFonctionObjectif();
		//La fonction objectif existe dans la matrice d'équations
		if (eqFctObj != null) {
			if (numeroEtape >= 0 && numeroEtape < historiqueMatriceAugmentee.size()) {
				ArrayList<Float> coefsFctObj = eqFctObj.getCoefficients();
				ArrayList<Float> solution = getSolutionBaseRealisableEtape(numeroEtape);
				Float valFctObj = 0.0f;
				
				/*
				 * On utilise que les variables hors base pour le calcul de la valeur
				 * de la fonction objectif
				 */
				for (int i = 0; i < dimension; i++) {
					Float sol = solution.get(i);
					Float coef = coefsFctObj.get(i);
					valFctObj += sol*coef;
				}
				
				return valFctObj;
			}
			else throw new IndiceIncorrectException("Tentative d'acces a une matrice en dehors des limites de historique : "+numeroEtape);
		}
		//La fonction objectif n'a pas été réglée, on renvoit une valeur par défaut 0.0
		else return 0.0f;
	}
}
